﻿#include <QApplication>
#include <QWidget>

//在Windows平台去掉黑窗口：
#pragma comment(linker,"/subSystem:console /entry:mainCRTStartup")
//如果想让黑窗口出来，就把Windows 改成 console


/*

QSqlQuery
		Sql查询
	  执行SQL语句
	QSqlQuery封装了从在QSqlDatabase上执行的SQL查询中创建、导航和检索数据所涉及的功能。 它可以用于执行DML(数据操作语言)语句，如SELECT、INSERT、UPDATE和DELETE，
	也可以用于执行DDL(数据定义语言)语句，如CREATE TABLE。 它也可以用于执行非标准SQL的特定数据库命令。
	成功执行的SQL语句将查询的状态设置为活动，以便isActive()返回true。 否则，查询的状态将设置为不活动。 在这两种情况下，当执行新的SQL语句时，查询将定位在无效的记录上。 
	在检索值之前，活动查询必须导航到有效记录(以便isValid()返回true)。


SQL准备语句
	经过测试SQLite不支持，Mysql支持
	QSqlQuery支持预先准备的查询执行和将参数值绑定到占位符。 有些数据库不支持这些特性，所以对于这些特性，Qt会模拟所需的功能。
		Oracle数据库使用冒号语法识别占位符，例如`:name`。
		ODBC仅仅使用`? `字符。
	Qt支持这两种语法，只是不能将它们混合在同一个查询中。

*/

#include <QSqlDatabase>
#include <QSqlerror>
#include <QDebug>
#include <QSqlRecord>
#include <QSqlQuery>
#include <iostream>


//使用mysql数据库或者sqlite数据库：
#define DatabaseOrSqlite 1
//是默认连接还是显示连接：
#define Default_connection 0
//通过名字或者位置来给占位符绑定值：
#define Name_Or_Pos 1
//通过命名或者 ？ 来写准本语句：
#define Christen_Or_Wenhao 0
class UseSqldatabase
{
public:
	UseSqldatabase()
	{
#if DatabaseOrSqlite
		connectDatabase();
#else
		connectSqlite();
#endif
		//execSql();

		preSql();

	}


	//连接数据库：
		//1.连接mysql：
	void connectDatabase()
	{
		//获取支持的驱动的列表：
		qInfo() << QSqlDatabase::drivers();		//QList("QSQLITE", "QMARIADB", "QMYSQL", "QODBC", "QPSQL")
		/*
		如果不进行配置，是没有MYSQL驱动的，要用visual studio打开目录 :\Qt\QT_new\6.5.2\Src\qtbase\src\plugins\sqldrivers；
																添加set(QT_FEATURE_sql_mysql ON),在生成CMakeLists.text文件；
		然后在静态库会生成在	D:\Qt\QT_new\6.5.2\Src\qtbase\src\plugins\sqldrivers\out\build\x64-Debug\plugins\sqldrivers（这是Debug版本）；或者
							D:\Qt\QT_new\6.5.2\Src\qtbase\src\plugins\sqldrivers\out\build\x64-Release\plugins\sqldrivers （这是Release版本）；
		把生成的qsqlmysqld.dll（这是Debug生成的）	和    qsqlmysql.dll（这是Release生成的）	复制到 D:\Qt\QT_new\6.5.2\msvc2019_64\plugins\sqldrivers文件目录下，

		还有一点要注意的是MySql的bin目录和lib目录一定要在系统环境变量中配置，不然也不会出现这个MYSQL驱动；
			
		我在第一次使用时，就没有这个驱动，后来才想起来我懂Debug生成的dll文件没有复制到msvc目录下，所以才会这样；突然想到，真意外啊；


		*/


		//添加连接：
#if Default_connection
		auto db = QSqlDatabase::addDatabase("QMYSQL");
#else
		auto db = QSqlDatabase::addDatabase("QMYSQL","mysql_con_1");
#endif

		//指定主机名：
		db.setHostName("localhost");	//IP地址
		//填端口号：
		db.setPort(3306);				//端口号
		//用户名：
		db.setUserName("root");
		//用户密码：
		db.setPassword("6666");
		//默认使用什么数据库名：
		db.setDatabaseName("test_course");

		//打开连接,判断是否打开成功：
		if (db.open())
		{
			qInfo() << "successfull";
		}
		else
		{
			qInfo() << "failed"<<db.lastError();
			return;
		}


		//获取数据库里面的所有的表名：		这个只能在mysql中使用；
		qInfo() << db.tables();

		//获取指定表的字段：
		auto record = db.record("emp");
		for (int i = 0; i < record.count(); i++)
		{
			qInfo() << record.fieldName(i);
		}
	}
		//2.连接sqlite：
	void connectSqlite()
	{
		//获取支持的驱动的列表：
		qInfo() << QSqlDatabase::drivers();		//QList("QSQLITE", "QMARIADB", "QMYSQL", "QODBC", "QPSQL")
		
		//添加连接：
		auto db = QSqlDatabase::addDatabase("QSQLITE");
		db.setDatabaseName("stu.db");

		//打开连接,判断是否打开成功：
		if (db.open())
		{
			qInfo() << "successfull";
		}
		else
		{
			qInfo() << "failed" << db.lastError();
			return;
		}

		//注意，这个会在bin文件目录下生成db文件；如果根目录的bin文件下没有，那就是在out的文件目录里的bin文件夹里；


		//获取数据库里面的所有的表名：		这个只能在mysql中使用；
		qInfo() << db.tables();

		//获取指定表的字段：
		auto record = db.record("emp");
		for (int i = 0; i < record.count(); i++)
		{
			qInfo() << record.fieldName(i);
		}
	}


	//执行sql语句：
	void execSql()
	{
		//在 connectDatabase 函数里面	已经链接了数据库，所以在这里可以直接使用，创建的默认连接；
#if Default_connection
		//1.默认连接：
		QSqlQuery sql;
#else
		//2.如果你在打开的时候，指定了连接名称，那么在后续的连接的时候，需要显示的获取连接：
		auto db = QSqlDatabase::database("mysql_con_1");		//要注意，连接名称要和前面的一致；
		QSqlQuery sql(db);
#endif
		if (!sql.exec("select * from emp"))
		{
			qInfo() << "failed>" << sql.lastError().text();
			return;
		}
		

		//获取查询结果：
				//获取字段数量：
		int filedCnt = sql.record().count();
			//输出表头：
		for (int i = 0; i < filedCnt; i++)
		{
			std::cout.width(15);
			std::cout.setf(std::ios::left);
			std::cout << sql.record().fieldName(i).toStdString();
		}
		std::cout << std::endl;
			//输出数据：
		while (sql.next())	//循环获取每条记录；
		{
			//获取每条记录的每个字段数据：
			for (int i = 0; i < filedCnt; i++)
			{
				//qInfo() << sql.value(i);		//能换行，看着很乱；

				std::cout.width(15);
				std::cout.setf(std::ios::left);
				std::cout << sql.value(i).toString().toStdString();
			}
			std::cout << std::endl;
		
		}
		std::cout << std::endl;

		//导航：
		if (sql.seek(3))		//获取第三行的数据内容
		{
			for (int i = 0; i < filedCnt; i++)
			{
				std::cout.width(15);
				std::cout.setf(std::ios::left);
				std::cout << sql.value(i).toString().toStdString();
			}
		}


		//获取查询到的记录数：		只针对查询
		qInfo() << sql.size();

		//获取影响到的行数：		更新，插入，删除
		qInfo() << sql.numRowsAffected();

		//sqlite是不报告大小的
	}

	//准备语句：
	void preSql()
	{
		auto db = QSqlDatabase::database("mysql_con_1");
		QSqlQuery query(db);

		

		//创建一个新的表：如果存在就不创建：

		QString sql;
		if (!db.tables().contains("qt_users"))
		{
			sql = "create table qt_users(id int,username varchar(20),nickname varchar(20))";
			if (!query.exec(sql))
			{
				qWarning() << "创建表失败" << query.lastError().text();
				return;
			}
		}
		

#if Christen_Or_Wenhao
			//1.写	命名		准备语句：
			query.prepare("insert into qt_users(id,username,nickname) values(:id,:username,:nickname) ");
			//1.2.给占位符绑定值
	#if Name_Or_Pos
				//1.2.1通过名字：
			query.bindValue(":id", 100);
			query.bindValue(":username", "lifei");
			query.bindValue(":nickname", "李飞");
	#else
				//1.2.2通过位置：
			query.bindValue(0, 200);
			query.bindValue(1, "liqi");
			query.bindValue(2, "李琪");
	#endif
#else
			//2.写	?		准备语句：
			query.prepare("insert into qt_users(id,username,nickname) values(?,?,?) ");
			//2.2.给占位符绑定值
	#if Name_Or_Pos
				//2.2.1通过顺序：
			query.addBindValue( 400);
			query.addBindValue( "lifei");
			query.addBindValue( "李飞");
	#else
				//2.2.2通过位置：
			query.bindValue(0, 300);
			query.bindValue(1, "liqi");
			query.bindValue(2, "李琪");
	#endif
#endif
		//执行语句：
		if (!query.exec())
		{
			qWarning() << "插入表失败" << query.lastError().text();
			return;
		}
		

	}

};

/*
数据库模型
	QSqlQueryModel
		QSqlQueryModel类为SQL结果集提供了	只读数据模型	。
		QSqlQueryModel是执行SQL语句和遍历结果集的高级接口。 它构建在较低级别的QSqlQuery之上，可用于向视图类(如QTableView)提供数据。
*/
#include <QSqlQueryModel>
#include <QTableView>
#include <QSqlTableModel>
#include <QPushButton>





class mySqlQueryModel 
{
public:
	QSqlDatabase db;
	QTableView view;

	QWidget btnwidget;			//按钮的基类；
	QPushButton btnSubmit;		//提交按钮	保存编辑过的view数据
	QPushButton btnRevert;		//撤销按钮	撤销操作；


public:
	mySqlQueryModel()
	{
		db = QSqlDatabase::addDatabase("QMYSQL");
		db.setHostName("127.0.0.1");
		db.setPort(3306);
		db.setDatabaseName("test_course");
		db.setUserName("root");
		db.setPassword("6666");
		if (!db.open())
		{
			qWarning() << "连接打开失败";
			return;
		}


		//创建新的窗口 实现对view的数据操作：
		btnwidget.resize(400, 300);
		btnwidget.setWindowTitle("对TableView的数据操作");
		btnSubmit.setParent(&btnwidget);
		btnSubmit.setText("保存");
		btnSubmit.move(50, 140);
		btnRevert.setParent(&btnwidget);
		btnRevert.setText("撤销");
		btnRevert.move(280, 140);;

		btnwidget.show();





		//useSqlQueryModel();

		sqlTableModel();
	}


	void useSqlQueryModel()
	{
		//创建模型：
		auto model = new QSqlQueryModel;

		//给模型设置查询：
		model->setQuery("select * from emp");

		//把模型交给视图：
		view.setModel(model);
		view.show();

	}

	void sqlTableModel()
	{
		auto model = new QSqlTableModel;

		//设置表明即可自动查询,会自动的设置表头；
		model->setTable("emp");

		//设置过滤：
		model->setFilter("sal < 3000");	//只显示工资小于3000的成员数据
		
		//还需要执行一下查询，才会有数据：
		model->select();

		view.setModel(model);
		view.show();

		QObject::connect(&btnSubmit, &QPushButton::clicked, model, &QSqlTableModel::submitAll);
		QObject::connect(&btnRevert, &QPushButton::clicked, model, &QSqlTableModel::revertAll);

	}



};

int main(int argc, char* argv[])
{
	QApplication a(argc, argv);

	//UseSqldatabase d;

	mySqlQueryModel s;

	return a.exec();
}


